#include <malloc.h>
#include "queue.h"
#include "string.h"


//private
int mod_add(u_int32_t a,u_int32_t b,u_int32_t mod){
    return ( a + b ) % mod;
}

int mod_sub(u_int32_t a,u_int32_t b,u_int32_t mod){
    return (( a - b ) + mod ) % mod;
}

int is_full(queue this){
    return this->size(this) == (this->cap-1);
}

int scale_up(queue this){
    void** new_array=(void **)calloc(this->cap*2,sizeof(void*));
    if(new_array==NULL)return -1;
    
    int old_size=this->size(this);
    for(int i=0;i<old_size;i++){
        new_array[i]=this->array[this->head];
        this->head=mod_add(this->head,1,this->cap);
    }
    free(this->array);
    this->array=new_array;
    this->cap *= INCREASE_FACTOR;
    this->head=0;
    this->rear=old_size;
    return 0;
}

int queue_is_empty(queue this);

int queue_size(queue this);

int queue_push(queue this,void* data);

void* queue_pop(queue this);

void* queue_top(queue this);

void queue_clear(queue this);

void destory_queue(queue this);

//public
queue create_queue(){
    queue this=(queue)calloc(1,sizeof(queue_t));
    this->cap=INIT_CAP;
    this->head=0;
    this->rear=0;
    this->array=(void**)calloc(INIT_CAP,sizeof(void*));
    
    this->is_empty=queue_is_empty;
    this->size=queue_size;
    this->push=queue_push;
    this->pop=queue_pop;
    this->top=queue_top;
    this->clear=queue_clear;
    this->destroy=destory_queue;
    return this;  
}


int queue_is_empty(queue this){
    return (this->size(this)==0);
}

int queue_size(queue this){
    return mod_sub(this->rear,this->head,this->cap);
}


int queue_push(queue this,void* data){
    if(this==NULL)return -1;
    if(is_full(this) && (scale_up(this)==-1))return -1;
    this->array[this->rear]=data;
    this->rear=mod_add(this->rear,1,this->cap);
    return 0;
}

void* queue_pop(queue this){
    if(this==NULL || this->is_empty(this)){
        return NULL;
    }
    void* data=this->array[this->head];
    this->head=mod_add(this->head,1,this->cap);
    return data;
}

void* queue_top(queue this){
    if(this==NULL || this->is_empty(this)){
        return NULL;
    }
    return this->array[this->head];
}

void queue_clear(queue this){
    if(this==NULL || this->is_empty(this)){
        return ;
    }
    this->rear=this->head;
}

void destory_queue(queue this){
    free(this->array);
    free(this);
}